package com.lnsystem.service.impl;

import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

import com.lnsystem.model.MovingAverage;
import com.lnsystem.model.StockDailyData;
import com.lnsystem.service.TechnicalIndicatorsService;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils.Collections;

@Component
public class TechnicalIndicatorsServiceImpl implements TechnicalIndicatorsService {

	Logger logger = Logger.getLogger(TechnicalIndicatorsServiceImpl.class);

	
	//TODO: would it make sense to  move all SMA, EMA, BB calculations to a different class
	// and have them as static methods?

	/**
     * {@inheritDoc} 
     */
	
	
	// we will use this method itself, instead of writing a method to compute SMA/EMA for a single stockDailyData item.. 
	// we will have dao method to get the stockList for a date range (so that it gets all data for the last 365 days).
	@Override
	public void calculateSMA(List<StockDailyData> dailyDataList, int days) { 
    	
		java.util.Collections.reverse(dailyDataList);
		
    	if(dailyDataList!=null && dailyDataList.size()>0 && days > 0) {
        	int size = dailyDataList.size();

        	if(days < size) {
    			double runnningSum = 0.00000;
    			for(int a = 0; a < days; a++) {
    				StockDailyData dailyData = (StockDailyData)dailyDataList.get(a);
    				runnningSum += dailyData.getClose();
    				
    				// instead of creating new object everytime,
    				// get existing MA object and set the MA values.
    				// if existing obj is null, create new object.
    				
    				MovingAverage ma  = dailyData.getMovingAverage(0,days); // 0 = SMA
    				if(ma == null) {
    					ma = new MovingAverage(0,days,runnningSum / (a+1));
        				dailyData.getMovingAverages().add(ma);
        				ma.setStockDailyData(dailyData);
    				} else {
    					if(ma.getMaValue() != (runnningSum / (a+1))) {
        					ma.setMaValue(runnningSum / (a+1));
    					}
    				}
    				//maList.set(a, ma);
    				//TODO uncomment below line if needed later...
    				////////////ma.setRunningSum(runnningSum);
    			}
    		
    			/*MovingAverage ma = new MovingAverage(0,days,runnningSum / days);
    			maList.set(days-1, ma);*/
    			
    			int index = days;
    			while(index < size) {
    				StockDailyData dailyData = (StockDailyData)dailyDataList.get(index);
    				runnningSum += dailyData.getClose();
    				StockDailyData dailyData1 = (StockDailyData)dailyDataList.get(index-days);
    			    runnningSum -= dailyData1.getClose();

    				MovingAverage ma  = dailyData.getMovingAverage(0,days);
    				if(ma == null) {
    					ma = new MovingAverage(0,days,runnningSum / days);
        			    dailyData.getMovingAverages().add(ma);
        			    ma.setStockDailyData(dailyData);
    				} else {
    					if(ma.getMaValue() != (runnningSum / days)) {
        					ma.setMaValue(runnningSum / days);
    					}    					
    				}  			    
    			    
    				//maList.set(a, ma);
    				//TODO uncomment below line if needed later...
    				////////////ma.setRunningSum(runnningSum);
    				
    			    index++;
    	
    			}
    	    } else {
    			for(int a = 0; a < size; a++) {
    				StockDailyData dailyData = (StockDailyData)dailyDataList.get(a);
    				MovingAverage ma = dailyData.getMovingAverage(0,days);
    				if(ma == null) {
    					ma = new MovingAverage(0,days,0);
    				} else {
    					ma.setMaValue(0);
    				}
    				//maList.set(a,ma);
    				dailyData.getMovingAverages().add(ma);
    				ma.setStockDailyData(dailyData);
    			}
    		}
    	}
		java.util.Collections.reverse(dailyDataList);
    }
    
    /**
     * {@inheritDoc} 
     */
	@Override
	public void calculateEMA(List<StockDailyData> stockList, int days) { 

    	if(stockList!=null && stockList.size()>0 && days > 0) {
    		int size = stockList.size();
    		double[] ma = new double[size];
    		
    	    if(days < size) {
	    		double runnningSum = 0.00000;
	    		for(int a = 0; a < days; a++) {
	    			StockDailyData dailyData = (StockDailyData)stockList.get(a);
	    			runnningSum += dailyData.getClose();
	    			
	    			MovingAverage movingAverage = dailyData.getMovingAverage(1, days);
	    			if(movingAverage == null) {
	    				movingAverage = new MovingAverage(1,days,runnningSum / (a+1));
		    			dailyData.getMovingAverages().add(movingAverage);
		    			movingAverage.setStockDailyData(dailyData);
	    			} else {
	    				
	    				if(movingAverage.getMaValue() != (runnningSum / (a+1))) {
		    				movingAverage.setMaValue(runnningSum / (a+1));
	    				}
					}
	    			
	    			////////////////movingAverage.setRunningSum(runnningSum);

	    			
	    		    ma[a] = runnningSum / (a+1);
	    		}
	    		// X = (K x (C - P)) + P (X = Current EMA, C = Current Price, P = Previous period's EMA*, K = Smoothing constant)
	    		int index = days;		
	    		double K = 2 / (double)(1 + days);
	    		while(index < size) {
	    			StockDailyData dailyData = (StockDailyData)stockList.get(index);
	    		    double C = dailyData.getClose();
	    		    double P = ma[index-1];
	    		    //logger.debug("K=" + K + " C=" + C + " P=" + P + " days=" + days);
	    		    ma[index] = (K * (C - P)) + P;	
	    		    

	    		    MovingAverage movingAverage = dailyData.getMovingAverage(1, days);
	    		    
	    		    if(movingAverage == null) {
	    		    	movingAverage = new MovingAverage(1,days,ma[index]);
		    		    dailyData.getMovingAverages().add(movingAverage);
		    			movingAverage.setStockDailyData(dailyData);
	    		    } else {
	    		    	if(movingAverage.getMaValue()!=ma[index]) {
		    		    	movingAverage.setMaValue(ma[index]);	    		    	
	    		    	}
	    		    }
	    		    //////////////////movingAverage.setRunningSum(runnningSum);
	    			index++;
	    		}		    
    	    }	
    	}

	}

	@Override
	public void calculateBB(List<StockDailyData> stockList, int maDays, double numDev) {
		// ma_days = simple moving average days
		// nof_devs = Number of Standard deviation to upper and lower band (usually 2)
		StockDailyData stock = null;
    	if(stockList!=null && stockList.size()>0 && maDays > 0 && numDev > 0 ) {
    		if(stockList!=null && stockList.size()>0){
    			
    			if(stockList.size()<maDays){
    				logger.error("Cannot proceed with BB calculation. Number of historical data items("+stockList.size()+") is less than maDays value:"+maDays); 
    				return;
    			}
    			
    			int size = (int)stockList.size();
    			double[] upper = new double[size];
    			double[] lower = new double[size];
    			double[] middle = new double[size];
    			double sum = 0.000;
    			double dev = 0.000;
    			double ma = 0;
    			for(int a = 0; a < maDays; a++) {
    				
    				stock = (StockDailyData)stockList.get(a);
    				
    				//get the sma20 value
    				for(MovingAverage movAvg : stock.getMovingAverages()){
    					if(movAvg.getType()==0 && movAvg.getPeriod()==20){
    						ma = movAvg.getMaValue();
    					}
    				}
    				
    				dev = stock.getClose() - ma;
    				dev *= dev; 
    				sum += dev;
    				lower[a] = ma - (Math.sqrt(sum / a) * numDev);
    				upper[a] = ma + (Math.sqrt(sum / a) * numDev);
    				middle[a] = ma;
    				
    				stock.setBbLower(lower[a]);
    				stock.setBbMiddle(middle[a]);
    				stock.setBbUpper(upper[a]);
    			}
    			sum = 0.000;
    			
    			for(int a = maDays; a < size; a++) {
    				for(int b = a - maDays ; b < a ; b++) {
    					stock = (StockDailyData)stockList.get(b);
    					
    					//get the sma20 value
    					for(MovingAverage movAvg : stock.getMovingAverages()){
    						if(movAvg.getType()==0 && movAvg.getPeriod()==20){
    							ma = movAvg.getMaValue();
    						}
    					}			
    					
    					dev = stock.getClose() - ma;
    					dev *= dev; 
    					sum += dev;
    				}
    				lower[a] = ma - (Math.sqrt(sum / maDays) * numDev);
    				upper[a] = ma + (Math.sqrt(sum / maDays) * numDev);
    				sum = 0.000;
    				middle[a] = ma;
    				
    				stock.setBbLower(lower[a]);
    				stock.setBbMiddle(middle[a]);
    				stock.setBbUpper(upper[a]);
    			}
    		   
    		} 		
    	}
		


	}
	
    /**
     * Calculates moving average just for one or more closing prices as they get added to the stocklist
     * 
     * @param stockList - updated StockDailyData list with one or more newly added closing price
     * @param days  - SMA/EMA days
     * @param type - 0 for SMA, 1 for EMA
     * @param runnningSum - running sum of closing price from the last SMA calculation
     * @param index - index of the closing price in the list for which SMA was last computed.
     */
/*	
    public void calculateMovingAverage(StockDailyData stock, int days, int type, int runnningSum, int index) { 
    	// TODO - the linkedlist parameter should get be like LinkedList<T> stockList
    	// where T can be StockDaily, or StockWeekly
    	double maValue;
    	//int size = stockList.size();
    	//while(index < size) {
		    //StockDailyData stock = (StockDailyData)stockList.get(index);
		    runnningSum += stock.getClose();
		    stock = (StockDailyData)stockList.get(index-days);
		    runningSum -= stock.getClose();
		    

		    //ma[index++] = runnningSum / days; // TODO - instead update the sma attribute of the stock itself.
		    maValue = runnningSum / days;
		    
		    //TODO add method here to set the maValue to StockDailyData..
		    
		//}
    	
    }	
	
*/	
	
	
}
